package com.digua.base.util

import android.content.Context
import android.content.SharedPreferences
import com.alibaba.fastjson2.JSON
import com.alibaba.fastjson2.JSONArray
import com.digua.base.base.App
import com.digua.base.config.BaseConfig
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable
import java.io.UnsupportedEncodingException
import java.net.URLDecoder
import java.net.URLEncoder

object SPUtils {
    //用户缓存
    val userCachePrefix = "SP_USER_"
    private var userId: String? = ""
    private var sp: SharedPreferences? = null

    private fun getSp(): SharedPreferences {
        if (sp == null) {
            sp = App.instance.getSharedPreferences(BaseConfig.SP_NAME, Context.MODE_PRIVATE)
        }
        return sp!!
    }

    fun getAll(): Map<String?, *>? = getSp().all
    fun getString(key: String, def: String = "") = getValue(key, def)
    fun getInt(key: String, def: Int = -1) = getValue(key, def)
    fun getLong(key: String, def: Long = -1L) = getValue(key, def)
    fun getFloat(key: String, def: Float = -1f) = getValue(key, def)
    fun getBoolean(key: String, def: Boolean = false) = getValue(key, def)

    /**
     * 保存数据到文件
     *
     * @param key
     * @param data
     */
    fun put(key: String, data: Any, isCommit: Boolean = false) {
        val edit = getSp().edit()
        when (data) {
            is Boolean, is String, is Number -> edit.putString(getKey(key), data.toString())
            else -> edit.putString(getKey(key), JSON.toJSONString(data))
        }
        if (isCommit) edit.commit() else edit.apply()
    }

    private fun <T> getValue(key: String, def: T): T {
        val value = try {
            getSp().getString(getKey(key), "null")
        } catch (e: Exception) {
            return when (def) {
                is Int -> getSp().getInt(getKey(key), def)
                is Float -> getSp().getFloat(getKey(key), def)
                is Long -> getSp().getLong(getKey(key), def)
                is Boolean -> getSp().getBoolean(getKey(key), def)
                is String -> getSp().getString(getKey(key), def)
                else -> def
            } as T
        }
        if (value == "null") return def
        return when (def) {
            is Int -> NumUtil.parseInt(value, def)
            is Float -> NumUtil.parseFlo(value, def)
            is Long -> NumUtil.parseLon(value, def)
            is Double -> NumUtil.parseDou(value, def)
            is Boolean -> value == "true"
            is String -> value
            else -> JSON.parse(value) as T
        } as T
    }

    /**
     * 从文件中读取数据
     *
     * @param key
     * @param def
     * @return
     */
    operator fun <T> get(key: String, def: T) = getSp().let {
        when (def) {
            is Int -> it.getInt(key, (def as Int))
            is Boolean -> it.getBoolean(key, (def as Boolean))
            is String -> it.getString(key, (def as String))
            is Float -> it.getFloat(key, (def as Float))
            is Long -> it.getLong(key, (def as Long))
            else -> deSerialization<T>(it.getString(key, serialize(def)))
        }
    }?.let { it } ?: def

    fun getStringSet(key: String, def: Set<String?>? = emptySet<String>()) = getSp().getStringSet(getKey(key), def)

    fun <T : Serializable?> getObj(key: String, clz: Class<T>?, def: T? = null): T? {
        val json = getString(getKey(key))
        return try {
            json?.let { JSON.parseObject(json, clz) } ?: def
        } catch (e: Exception) {
            def
        }
    }

    fun <T : Serializable?> getListObj(key: String, clz: Class<T>?, def: ArrayList<T>? = null): ArrayList<T>? {
        val json = getString(getKey(key))
        if (StrUtil.isBlank(json)) return def
        return try {
            json.let { JSONArray.parseArray(json, clz) } ?: def
        } catch (e: Exception) {
            def
        } as ArrayList<T>
    }

    operator fun contains(key: String) = getSp().contains(getKey(key))

    fun remove(key: String, isCommit: Boolean = false) = saveData(isCommit) { it.remove(getKey(key)) }

    fun clear(isCommit: Boolean = false) = saveData(isCommit) { it.clear() }

    private fun isSpace(s: String?): Boolean {
        if (s == null) return true
        var i = 0
        val len = s.length
        while (i < len) {
            if (!Character.isWhitespace(s[i])) {
                return false
            }
            ++i
        }
        return true
    }

    private fun getKey(key: String): String {
        val userId = getUserId()
        return if (StrUtil.isNotBlank(userId) && StrUtil.startsWithIgnoreCase(key, userCachePrefix) && !StrUtil.endWithIgnoreCase(key, userId)) {
            key + "_" + userId
        } else key
    }

    private fun setUserId(userId: String?) {
        SPUtils.userId = if (StrUtil.isBlank(userId)) "" else userId
        getSp().edit().putString("userId", SPUtils.userId).apply()
    }

    private fun getUserId(): String? {
        if (StrUtil.isBlank(userId)) {
            userId = getSp().getString("userId", "")
        }
        return userId
    }

    /**
     * 序列化对象
     *
     * @param *
     * @return *
     * @throws IOException
     */
    private fun <T> serialize(obj: T): String? {
        val byteArrayOutputStream = ByteArrayOutputStream()
        var objectOutputStream: ObjectOutputStream? = null
        var serStr: String? = ""
        try {
            objectOutputStream = ObjectOutputStream(byteArrayOutputStream)
            objectOutputStream.writeObject(obj)
            serStr = byteArrayOutputStream.toString("ISO-8859-1")
            serStr = URLEncoder.encode(serStr, "UTF-8")
            objectOutputStream.close()
            byteArrayOutputStream.close()
        } catch (e: IOException) {
            LogUtil.e("异常:", e)
        }
        return serStr
    }

    /**
     * 反序列化对象
     *
     * @param str *
     * @return *
     * @throws IOException            *
     * @throws ClassNotFoundException
     */
    private fun <T> deSerialization(str: String?): T? {
        var redStr: String? = null
        var obj: T? = null
        var byteArrayInputStream: ByteArrayInputStream? = null
        try {
            redStr = URLDecoder.decode(str, "UTF-8")
            byteArrayInputStream = ByteArrayInputStream(redStr.toByteArray(charset("ISO-8859-1")))
            val objectInputStream = ObjectInputStream(byteArrayInputStream)
            obj = objectInputStream.readObject() as T
            objectInputStream.close()
            byteArrayInputStream.close()
        } catch (e: UnsupportedEncodingException) {
            LogUtil.e("异常:", e)
        } catch (e: ClassNotFoundException) {
            LogUtil.e("异常:", e)
        } catch (e: IOException) {
            LogUtil.e("异常:", e)
        }
        return obj
    }

    private fun saveData(commit: Boolean, lambda: (editor: SharedPreferences.Editor) -> SharedPreferences.Editor) {
        if (commit) {
            lambda(getSp().edit()).commit()
        } else {
            lambda(getSp().edit()).apply()
        }
    }
}
